package com.autumn.zero.authorization.application.services.impl;

import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.autumn.common.api.domain.values.StringConstantItemValue;
import com.autumn.mybatis.criterion.EntityQuery;
import com.autumn.mybatis.criterion.LockModeEnum;
import com.autumn.mybatis.criterion.Query;
import com.autumn.mybatis.criterion.SpecifyUpdate;
import com.autumn.util.DataRollbackException;
import com.autumn.util.ExceptionUtils;
import com.autumn.validation.MatchesUtils;
import com.autumn.zero.application.services.AbstractSimpleZeroEditAppService;
import com.autumn.zero.authorization.application.dto.modules.PermissionNameCheckInput;
import com.autumn.zero.authorization.application.dto.modules.ResourcesInput;
import com.autumn.zero.authorization.application.dto.modules.ResourcesModulePermissionDto;
import com.autumn.zero.authorization.application.services.IResourcesModulePermissionAppService;
import com.autumn.zero.authorization.constants.OperationPermissionConstants;
import com.autumn.zero.authorization.entitys.modules.ResourcesModule;
import com.autumn.zero.authorization.entitys.modules.ResourcesModulePermission;
import com.autumn.zero.authorization.entitys.roles.RolePermission;
import com.autumn.zero.authorization.entitys.users.UserPermission;
import com.autumn.zero.authorization.repositorys.modules.IResourcesModulePermissionRepository;
import com.autumn.zero.authorization.repositorys.modules.IResourcesModuleRepository;
import com.autumn.zero.authorization.repositorys.roles.IRolePermissionRepository;
import com.autumn.zero.authorization.repositorys.users.IUserPermissionRepository;
import com.autumn.zero.authorization.services.IResourcesService;

/**
 * 资源权限模块应用服务
 * 
 * @author 老码农 2018-12-09 13:29:21
 */
public class ResourcesModulePermissionAppServiceImpl extends
		AbstractSimpleZeroEditAppService<Long, ResourcesModulePermission, IResourcesModulePermissionRepository, ResourcesModulePermissionDto, ResourcesModulePermissionDto>
		implements IResourcesModulePermissionAppService {

	@Autowired
	protected IResourcesModulePermissionRepository resourcesModulePermissionRepository;

	@Autowired
	protected IResourcesModuleRepository resourcesModuleRepository;

	@Autowired
	protected IUserPermissionRepository userPermissionRepository;

	@Autowired
	protected IRolePermissionRepository rolePermissionRepository;
	
	@Autowired
	protected IResourcesService resourcesService;

	@Override
	protected IResourcesModulePermissionRepository getRepository() {
		return this.resourcesModulePermissionRepository;
	}

	private Set<String> searchMembers = new HashSet<>();

	@Override
	protected Set<String> getSearchMembers() {
		if (searchMembers.size() == 0) {
			searchMembers.add(ResourcesModulePermission.FILED_NAME);
			searchMembers.add(ResourcesModulePermission.FILED_RESOURCES_ID);
		}
		return searchMembers;
	}

	@Override
	protected void queryByOrder(EntityQuery<ResourcesModulePermission> query) {
		query.orderBy(ResourcesModulePermission.FILED_RESOURCES_ID).orderBy(ResourcesModulePermission.FILED_SORT_ID)
				.orderBy(ResourcesModulePermission.FILED_NAME);
	}

	@Override
	public boolean existPermissionName(PermissionNameCheckInput input) {
		ExceptionUtils.checkNotNull(input, "input");
		input.valid();
		EntityQuery<ResourcesModulePermission> query = new EntityQuery<>(ResourcesModulePermission.class);
		query.eq(ResourcesModulePermission.FILED_RESOURCES_ID, input.getResourcesId().trim())
				.eq(ResourcesModulePermission.FILED_NAME, input.getName().trim());
		return query.exist(this.resourcesModulePermissionRepository);
	}

	/**
	 * 
	 * @param input
	 * @param checkName
	 * @param checkfriendlyName
	 * @param checkSortId
	 * @param query
	 */
	private void checkEdit(ResourcesModulePermissionDto input, boolean checkResources, boolean checkName,
			boolean checkfriendlyName, boolean checkSortId, EntityQuery<ResourcesModulePermission> query) {
		if (!MatchesUtils.isNumberOrLetterOrChinese(input.getName())) {
			ExceptionUtils.throwValidationException("权限名称只能包含中文、字母和数字。");
		}
		ResourcesModule module = resourcesModuleRepository.get(input.getResourcesId());
		if (module == null) {
			ExceptionUtils.throwValidationException("引用的资源[" + input.getResourcesId() + "]不存在。");
		}
		if (!module.getIsAuthorize()) {
			ExceptionUtils.throwValidationException("引用的资源名称[" + module.getName() + "]不支持权限配置, isAuthorize 为 false。");
		}
		if (checkName) {
			query.reset();
			query.eq(ResourcesModulePermission.FILED_RESOURCES_ID, input.getResourcesId())
					.eq(ResourcesModulePermission.FILED_NAME, input.getName()).lock(LockModeEnum.UPDATE);
			if (query.countByWhere(this.getRepository()) > 0) {
				ExceptionUtils.throwValidationException(
						String.format("相同的资源id[%s]与权限名称[%s]已重复。", input.getResourcesId(), input.getName()));
			}
		}
		if (checkfriendlyName) {
			query.reset();
			query.eq(ResourcesModulePermission.FILED_RESOURCES_ID, input.getResourcesId())
					.eq(ResourcesModulePermission.FILED_FRIENDLY_NAME, input.getFriendlyName())
					.lock(LockModeEnum.UPDATE);
			if (query.countByWhere(this.getRepository()) > 0) {
				ExceptionUtils.throwValidationException(
						String.format("相同的资源id[%s]与权限友好名称[%s]已重复。", input.getResourcesId(), input.getFriendlyName()));
			}
		}
		if (checkSortId) {
			query.reset();
			query.eq(ResourcesModulePermission.FILED_RESOURCES_ID, input.getResourcesId())
					.eq(ResourcesModulePermission.FILED_SORT_ID, input.getSortId()).lock(LockModeEnum.UPDATE);
			if (query.countByWhere(this.getRepository()) > 0) {
				ExceptionUtils.throwValidationException(
						String.format("相同的资源id[%s]与显示顺序[%s]已重复。", input.getResourcesId(), input.getFriendlyName()));
			}
		}

	}

	@Override
	protected ResourcesModulePermission addBefore(ResourcesModulePermissionDto input,
			EntityQuery<ResourcesModulePermission> query) {
		this.checkEdit(input, true, true, true, true, query);
		return super.addBefore(input, query);
	}
	
	

	@Override
	protected ResourcesModulePermissionDto addAfter(ResourcesModulePermissionDto input,
			ResourcesModulePermission entity, EntityQuery<ResourcesModulePermission> query) {
		resourcesService.clearModulePermissionCache();
		return super.addAfter(input, entity, query);
	}

	@Override
	protected void updateBefore(ResourcesModulePermissionDto input, ResourcesModulePermission entity,
			EntityQuery<ResourcesModulePermission> query) {
		boolean equalsResources = entity.getResourcesId().equalsIgnoreCase(input.getResourcesId());
		boolean checkName = !(equalsResources && entity.getName().equalsIgnoreCase(input.getName()));
		boolean checkfriendlyName = !(equalsResources
				&& entity.getFriendlyName().equalsIgnoreCase(input.getFriendlyName()));
		boolean checkSortId = !(equalsResources && entity.getSortId().equals(input.getSortId()));
		this.checkEdit(input, !equalsResources, checkName, checkfriendlyName, checkSortId, query);
		super.updateBefore(input, entity, query);
	}

	@Override
	protected ResourcesModulePermissionDto updateAfter(ResourcesModulePermissionDto input,
			ResourcesModulePermission entity, ResourcesModulePermission oldEntity,
			EntityQuery<ResourcesModulePermission> query) {
		// 是否更新用户与角色引用的权限名称
		boolean isUpdate = !(entity.getResourcesId().equalsIgnoreCase(oldEntity.getResourcesId())
				&& entity.getName().equalsIgnoreCase(oldEntity.getName()));
		if (isUpdate) {
			// 更新用户引用的权限
			SpecifyUpdate su = new SpecifyUpdate(UserPermission.class);
			su.eq(UserPermission.FILED_RESOURCES_ID, oldEntity.getResourcesId())
					.eq(UserPermission.FILED_NAME, oldEntity.getName())
					.set(UserPermission.FILED_RESOURCES_ID, entity.getResourcesId())
					.set(UserPermission.FILED_NAME, entity.getName());
			this.userPermissionRepository.updateBySpecify(su.builderSection());

			// 更新角色引用的权限
			su = new SpecifyUpdate(RolePermission.class);
			su.eq(RolePermission.FILED_RESOURCES_ID, oldEntity.getResourcesId())
					.eq(RolePermission.FILED_NAME, oldEntity.getName())
					.set(RolePermission.FILED_RESOURCES_ID, entity.getResourcesId())
					.set(RolePermission.FILED_NAME, entity.getName());
			this.rolePermissionRepository.updateBySpecify(su.builderSection());
		}
		resourcesService.clearModulePermissionCache();
		return super.updateAfter(input, entity, oldEntity, query);
	}

	@Override
	protected void deleteBefore(Long id) {
		super.deleteBefore(id);
		ResourcesModulePermission module = getRepository().get(id);
		if (module != null) {
			// 删除用户引用的权限
			Query query = new Query(UserPermission.class);
			query.eq(UserPermission.FILED_RESOURCES_ID, module.getResourcesId()).eq(UserPermission.FILED_NAME,
					module.getName());
			this.userPermissionRepository.deleteByWhere(query.builderSection());

			// 删除角色引用的权限
			query = new Query(RolePermission.class);
			query.eq(RolePermission.FILED_RESOURCES_ID, module.getResourcesId()).eq(RolePermission.FILED_NAME,
					module.getName());
			this.resourcesModulePermissionRepository.deleteByWhere(query.builderSection());
		}
	}	
	

	@Override
	protected void deleteAfter(Long id, boolean isSoftDelete) {
		resourcesService.clearModulePermissionCache();
		super.deleteAfter(id, isSoftDelete);
	}

	/**
	 * 添加权限
	 * 
	 * @param resourcesId
	 * @param name          {@link com.autumn.zero.authorization.constants.OperationPermissionConstants}
	 * @param sortId
	 * @param permissionUrl
	 */
	private void addPermission(String resourcesId, String name, int sortId, String permissionUrl) {
		EntityQuery<ResourcesModulePermission> query = new EntityQuery<>(ResourcesModulePermission.class);
		query.eq(ResourcesModulePermission.FILED_RESOURCES_ID, resourcesId).eq(ResourcesModulePermission.FILED_NAME,
				name);
		if (query.countByWhere(this.getRepository()) == 0) {
			StringConstantItemValue item = OperationPermissionConstants.getItem(name);
			ResourcesModulePermission module = new ResourcesModulePermission();
			module.setFriendlyName(item.getName());
			module.setName(item.getValue());
			module.setResourcesId(resourcesId);
			module.setSortId(sortId);
			module.setPermissionUrl(permissionUrl);
			module.setSummary(item.getExplain());
			this.getRepository().insert(module);
		}
	}

	private void checkResources(String resourcesId) {
		ResourcesModule module = resourcesModuleRepository.get(resourcesId);
		if (module == null) {
			ExceptionUtils.throwValidationException("引用的资源Id[" + resourcesId + "]不存在。");
		}
		if (!module.getIsAuthorize()) {
			ExceptionUtils.throwValidationException("引用的资源名称[" + module.getName() + "]不支持权限配置, isAuthorize 为 false。");
		}
	}

	private void addQueryPermission(String resourcesId) {
		this.addPermission(resourcesId, OperationPermissionConstants.QUERY, 100,
				"/queryById\r\n/queryByList\r\n/queryByPageList");
		this.addPermission(resourcesId, OperationPermissionConstants.EXPORT, 101, "/downloadByExcel");
	}

	@Override
	@Transactional(rollbackFor = DataRollbackException.class)
	public void addDefaultQueryPermission(ResourcesInput input) {
		ExceptionUtils.checkNotNull(input, "input");
		input.valid();
		this.checkResources(input.getResourcesId());
		this.addQueryPermission(input.getResourcesId());
		resourcesService.clearModulePermissionCache();
	}

	@Override
	@Transactional(rollbackFor = DataRollbackException.class)
	public void addDefaultEditPermission(ResourcesInput input) {
		ExceptionUtils.checkNotNull(input, "input");
		input.valid();
		this.checkResources(input.getResourcesId());
		this.addPermission(input.getResourcesId(), OperationPermissionConstants.ADD, 50, "/add");
		this.addPermission(input.getResourcesId(), OperationPermissionConstants.UPDATE, 51, "/update");
		this.addPermission(input.getResourcesId(), OperationPermissionConstants.DELETE, 52, "/deleteById");
		this.addQueryPermission(input.getResourcesId());
		resourcesService.clearModulePermissionCache();
	}

	@Override
	public String getModuleName() {
		return "资源权限";
	}

}
